import random
import statistics

def get_size(n: int) -> int:
    return 13 - 2 * abs(7 - n)

def get_score(n: int) -> float:
    return 1 / get_size(n)

def roll() -> int:
    return random.randint(1, 6)

def round() -> tuple[tuple[int, int]]:
    stack = [roll() for _ in range(4)]
    group0 = (stack[0] + stack[1], stack[2] + stack[3])
    group1 = (stack[0] + stack[2], stack[1] + stack[3])
    group2 = (stack[0] + stack[3], stack[1] + stack[2])
    return (group0, group1, group2)

def count_frequency(round_n: int):
    data = {i: 0 for i in range(2, 13)}
    for _ in range(round_n):
        groups = round()
        for group in groups:
            for value in group:
                data[value] += 1
    return data

def can_pick(group: tuple, last: dict[int, int], not_available: list[int] = None) -> bool:
    if not_available is None:
        not_available = []

    for k, v in last.items():
        if get_size(k) == v:
            not_available.append(k)

def random_pick(groups: tuple[tuple[int, int]], last: dict[int, int]) -> tuple[int, int]:
    if len(last) == 3:
        valid_groups = []
        for group in groups:
            if (group[0] in last) or (group[1] in last):
                valid_groups.append(group)
        if len(valid_groups) == 0:
            return None
        else:
            return random.choice(valid_groups)
    else:
        return random.choice(groups)
    

def simulate_round(last: dict[int, int]) -> tuple[bool, dict[int, int]]:
    this = last.copy()
    group = random_pick(round(), last)
    if group is None:
        return (False, this)

    not_used = []
    for value in group:
        if value in this:
            this[value] += 1
        else:
            not_used.append(value)

    if len(this) == 3:
        return (True, this)

    if not_used:
        if len(this) == 2:
            if len(not_used) == 1:
                this[not_used[0]] = 1
            elif not_used[0] == not_used[1]:
                this[not_used[0]] = 2
            else:
                this[random.choice(not_used)] = 1
        else:
            if len(not_used) == 1:
                this[not_used[0]] = 1
            elif not_used[0] == not_used[1]:
                this[not_used[0]] = 2
            else:
                this = {not_used[0]: 1, not_used[1]: 1, **this}
    return (True, this)

def simulate_to_end() -> tuple[frozenset[int], float, int]:
    result = {}
    run = True
    n = 0
    while run:
        run, result = simulate_round(result)
        n += 1
    score = 0
    for k, v in result.items():
        score += get_score(k) * v
    return (frozenset(result), score, n)

def group_by_score(n: int):
    scores = {}
    ns = {}
    for _ in range(n):
        value_type, score, n_ = simulate_to_end()
        scores.setdefault(value_type, []).append(score)
        ns.setdefault(value_type, []).append(n_)
    scores_average = {k: statistics.fmean(v) for k, v in scores.items()}
    n_average = {k: statistics.fmean(v) for k, v in ns.items()}
    return {','.join([str(j) for j in k]): (f'{v:.3f}', f'{n_average[k]:.2f}') for k, v in sorted(scores_average.items(), key=lambda i: i[1], reverse=True)}

if __name__ == '__main__':
    data = group_by_score(10000)
    print(data)